/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package graphdisplay;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

/**
 *
 * @author Jimmy
 */
public class GraphDisplayFrame extends JFrame {

    private final int GRAPH_VIEW_MAX_X = 1150;
    private final int GRAPH_VIEW_MAX_Y = 550;
    private final int GRAPH_VIEW_MIN_X = 15;
    private final int GRAPH_VIEW_MIN_Y = 15;
    private JTextField nodeLabelTextField;
    private JTextField nodeXCoordinateTextField;
    private JTextField nodeYCoordinateTextField;
    private JButton addNodeButton;
    private JTextField edgeNode1TextField;
    private JTextField edgeNode2TextField;
    private JTextField edgeNodeWeightTextField;
    private JButton addEdgeButton;
    private JTextField pathStartNodeTextField;
    private JTextField pathEndNodeTextField;
    private JButton calcShortestPathButton;
    private JButton clearShortestPathButton;
    private JButton clearGraphButton;
    private JButton importButton;
    private JButton exportButton;
    private JButton generateNodesButton;
    public JLabel filePathLabel;
    List<Node> nodeList;
    List<WeightedEdge> edgeList;
    private WeightedGraph<Node> graph; //= new WeightedGraph<Vertex>(edges,vertices);
    private ShortestPathGraphView view; //= new GraphView(graph);
    private JScrollPane graphDisplayScrollPane;
    
    private boolean isGenerating = false;
    long numOfGenNodes = 1;

    GraphDisplayFrame() {
        initComponents();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        
        Thread genNodes = new Thread(new Runnable() {
            @Override
            public void run() {

                while (true) {
                    Random generator = new Random(new Date().getTime());
                    while (isGenerating) {
                        graph.addVertex(new Node(String.valueOf(numOfGenNodes), generator.nextInt(1150), generator.nextInt(550)));
                        view.addNodesToGraph();
                        view.repaint();
                        numOfGenNodes++;
                        try{
                            Thread.sleep(2);
                        }catch(Exception e){
                            
                        }                        
                    }
                }
            }
        });
        
        genNodes.setDaemon(true);
        genNodes.start();
    }

    private void initComponents() {

        nodeLabelTextField = new JTextField(5);
        nodeXCoordinateTextField = new JTextField(5);
        nodeYCoordinateTextField = new JTextField(5);
        addNodeButton = new JButton("Add Node");

        edgeNode1TextField = new JTextField(5);
        edgeNode2TextField = new JTextField(5);
        edgeNodeWeightTextField = new JTextField(5);
        addEdgeButton = new JButton("Add Edge");

        pathStartNodeTextField = new JTextField(5);
        pathEndNodeTextField = new JTextField(5);
        calcShortestPathButton = new JButton("Shortest Path");
        clearShortestPathButton = new JButton("Clear Path");
        clearGraphButton = new JButton("Clear Graph");

        importButton = new JButton("Import");
        exportButton = new JButton("Export");
        generateNodesButton = new JButton("Generate Nodes");
        filePathLabel = new JLabel("File Path");

        nodeList = new ArrayList<Node>();
        edgeList = new ArrayList<WeightedEdge>();

        JPanel addNodePanel = new JPanel(new GridLayout(4, 2));
        addNodePanel.add(new JLabel("Node label: "));
        addNodePanel.add(nodeLabelTextField);
        addNodePanel.add(new JLabel("x-coordinate: "));
        addNodePanel.add(nodeXCoordinateTextField);
        addNodePanel.add(new JLabel("y-coordinate: "));
        addNodePanel.add(nodeYCoordinateTextField);
        addNodePanel.add(new JLabel());
        addNodePanel.add(addNodeButton);
        addNodePanel.setBorder(new TitledBorder("Add a new node"));

        JPanel addEdgePanel = new JPanel(new GridLayout(4, 2));
        addEdgePanel.add(new JLabel("Node Label:"));
        addEdgePanel.add(edgeNode1TextField);
        addEdgePanel.add(new JLabel("Node Label:"));
        addEdgePanel.add(edgeNode2TextField);
        addEdgePanel.add(new JLabel("Weight:"));
        addEdgePanel.add(edgeNodeWeightTextField);
        addEdgePanel.add(new JLabel(""));
        addEdgePanel.add(addEdgeButton);
        addEdgePanel.setBorder(new TitledBorder("Add a new edge"));



        JPanel calcShortestPathPanel = new JPanel(new GridLayout(4, 2));
        calcShortestPathPanel.add(new JLabel("Starting Node Label: "));
        calcShortestPathPanel.add(pathStartNodeTextField);
        calcShortestPathPanel.add(new JLabel("Ending Node Label: "));
        calcShortestPathPanel.add(pathEndNodeTextField);
        calcShortestPathPanel.add(new JLabel(""));
        calcShortestPathPanel.add(calcShortestPathButton);
        calcShortestPathPanel.add(new JLabel(""));
        calcShortestPathPanel.add(clearShortestPathButton);
        calcShortestPathPanel.setBorder(new TitledBorder("Find a Shortest Path"));

        JPanel importExportPanel = new JPanel(new GridLayout(4, 2));
        importExportPanel.add(importButton);
        importExportPanel.add(exportButton);
        //importExportPanel.add(filePathLabel);
        importExportPanel.add(clearGraphButton);
        //importExportPanel.add(Box.createRigidArea(new Dimension(0, 5)));
        importExportPanel.add(generateNodesButton);                
        importExportPanel.setBorder(new TitledBorder("General Functions"));

        JPanel functionPanel = new JPanel(new GridLayout(1, 2));
        functionPanel.add(addNodePanel);
        functionPanel.add(addEdgePanel);
        functionPanel.add(calcShortestPathPanel);
        functionPanel.add(importExportPanel);
        functionPanel.setPreferredSize(new Dimension(1200, 125));
        functionPanel.setMaximumSize(new Dimension(1200, 125));
        functionPanel.setMinimumSize(new Dimension(1200, 125));

        //JPanel mainFramePanel = new JPanel(new BorderLayout());
        JPanel mainFramePanel = new JPanel();
        mainFramePanel.setLayout(new BoxLayout(mainFramePanel, BoxLayout.Y_AXIS ));        
        //mainFramePanel.setMaximumSize(new Dimension(1200, 100));

        addDefaultNodes();
        addDefaultEdges();
        addButtonListeners();

        graph = new WeightedGraph<Node>(edgeList, nodeList);
        view = new ShortestPathGraphView(graph);
        
        view.setLayout(null);
        graphDisplayScrollPane = new JScrollPane(view);
        graphDisplayScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        graphDisplayScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        graphDisplayScrollPane.setPreferredSize(new Dimension(1200, 600));
        graphDisplayScrollPane.setMaximumSize(new Dimension(1200, 600));
        graphDisplayScrollPane.setMinimumSize(new Dimension(1200, 600));
        
        //graphDisplayScrollPane.setBorder(BorderFactory.createTitledBorder(""));
        // graphDisplayScrollPane.setMaximumSize(new Dimension(800, 800));
        view.setGraphViewScrollPane(graphDisplayScrollPane);
        view.addNodesToGraph();
        mainFramePanel.add(graphDisplayScrollPane);
        mainFramePanel.add(functionPanel);
        //add(graphDisplayScrollPane);
        //add(mainFramePanel, BorderLayout.SOUTH);
        add(mainFramePanel);

        setSize(1200, 775);

    }

    void addButtonListeners() {

        addNodeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                addNodeAction(e);
            }
        });  
        
        addEdgeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                addEdgeAction(e);
            }
        });
        
        calcShortestPathButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                calcShortestPathAction(e);
            }
        });
        
        clearShortestPathButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                view.isShortestPathVisible(false);
                view.repaint();
            }
        });
        
        importButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                importAction(e);
            }
        });
        
        exportButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                exportAction(e);
            }
        });
        
        generateNodesButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                generateNodesAction(e);
            }
        });
        
        clearGraphButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                clearGraphAction(e);
            }
        });
    }   

    void addNodeAction(ActionEvent e) {
        String name = nodeLabelTextField.getText();

        if (name.length() > 0) {
            int x = 0;
            int y = 0;
            boolean isCorrectFormat = true;
            try {
                x = Integer.parseInt(nodeXCoordinateTextField.getText().trim());
                y = Integer.parseInt(nodeYCoordinateTextField.getText().trim());
                
                if(x > GRAPH_VIEW_MAX_X || x < GRAPH_VIEW_MIN_X || y > GRAPH_VIEW_MAX_Y || y < GRAPH_VIEW_MIN_Y){
                    JOptionPane.showMessageDialog(null, "X-coordinate must be between 15 - 1150.\n Y-coordinate must be between 15 - 550.", "", JOptionPane.WARNING_MESSAGE);
                }else{
                    boolean isInGraph = false;
                    for(Node n : graph.getVertices()){
                        if(name.equals(n.getName())){
                            isInGraph = true;
                        }
                    }
                    if(!isInGraph){
                        graph.addVertex(new Node(name, x, y));
                    view.addNodesToGraph();
                    }else{
                        JOptionPane.showMessageDialog(null, "A node already exists with this label.\n You must choose a label name that does not already exist.", "", JOptionPane.WARNING_MESSAGE);
                    }                    
                }
            } catch (java.lang.NumberFormatException ne) {
                isCorrectFormat = false;
                System.out.println(ne);
            }
            if (!isCorrectFormat || x <= 0 || y <= 0) {
                JOptionPane.showMessageDialog(null, "X, Y Coordinates must be positive integers.", "", JOptionPane.WARNING_MESSAGE);
            }
            view.repaint();
        } else {
            JOptionPane.showMessageDialog(null, "A node label must be provided.", "", JOptionPane.WARNING_MESSAGE);
        }
    }
    
    void addEdgeAction(ActionEvent e) {
        String nodeStr1 = edgeNode1TextField.getText().trim();
        String nodeStr2 = edgeNode2TextField.getText().trim();
        Node node1 = null;
        Node node2 = null;
        int numOfNodeExist = 0;
        try {
            double weight = Double.parseDouble(edgeNodeWeightTextField.getText().trim());
            for (Node n : graph.getVertices()) {
                if (nodeStr1.equals(n.getName())) {
                    numOfNodeExist++;
                    node1 = n;
                    
                }
                if(nodeStr2.equals(n.getName())){
                    numOfNodeExist++;
                    node2 = n;
                    
                }
            }
            if(numOfNodeExist == 2){
                if(nodeStr1.equals(nodeStr2)){
                    JOptionPane.showMessageDialog(null, "Nodes must be different to add edge.", "", JOptionPane.WARNING_MESSAGE);
                }else{
                    boolean isUnique = true;
                    for(WeightedEdge ee : edgeList){
                        if((ee.u == nodeList.indexOf(node1) && ee.v == nodeList.indexOf(node2)) || (ee.v == nodeList.indexOf(node1) && ee.u == nodeList.indexOf(node2))){
                            isUnique = false;
                        }
                    }
                    if (isUnique) {                       
                        edgeList.add(new WeightedEdge(nodeList.indexOf(node1), nodeList.indexOf(node2), (int) weight));
                        graph.addEdge(nodeList.indexOf(node1), nodeList.indexOf(node2), (int) weight);
                    }else{
                        JOptionPane.showMessageDialog(null, "Edge already exists.", "", JOptionPane.WARNING_MESSAGE);
                    }                   
                }
            }
        } catch (java.lang.NumberFormatException ne) {
            JOptionPane.showMessageDialog(null, "Edge weight must be of double format.", "", JOptionPane.WARNING_MESSAGE);
        }
        view.repaint();
    }
    
    void calcShortestPathAction(ActionEvent e){
        
        String nodeStr1 = pathStartNodeTextField.getText().trim();
        String nodeStr2 = pathEndNodeTextField.getText().trim();
        
        Node node1 = null;
        Node node2 = null;
        int numOfNodeExist = 0;
        for (Node n : graph.getVertices()) {
                if (nodeStr1.equals(n.getName())) {
                    numOfNodeExist++;
                    node1 = n;
                    
                }
                if(nodeStr2.equals(n.getName())){
                    numOfNodeExist++;
                    node2 = n;
                    
                }
            }
        
        try {    	  
            WeightedGraph<String> graph1 = new WeightedGraph<String>(edgeList, nodeList.size());
            WeightedGraph<String>.ShortestPathTree tree1 = graph1.getShortestPath(nodeList.indexOf(node1));
            java.util.List<Integer> path = tree1.getPath1(nodeList.indexOf(node2));
            view.setTree1(true, path, nodeList);
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        view.repaint();
    }
    
    void importAction(ActionEvent e){
        JFileChooser chooser = new JFileChooser();
        chooser.showOpenDialog(exportButton);
        File file = chooser.getSelectedFile();
        
        //Clear contents of vertex and edge lists
        edgeList.clear();
        nodeList.clear();        
        
        //Read from file
        try {
            
            //Get connection to file -> pass the file path into the FileReader object
            FileReader ifstream = new FileReader(file.getAbsolutePath());
            BufferedReader in = new BufferedReader(ifstream);

            String fileLine = in.readLine();
            
            String[] fileLineArray = fileLine.split(":");            
            int numVertices = Integer.parseInt(fileLineArray[1]);           

            for (int i = 0; i < numVertices; i++) {
                if (in.read() != -1) {
                    fileLineArray = in.readLine().split(",");
                    Node vertex = new Node(fileLineArray[0], Integer.parseInt(fileLineArray[1]),Integer.parseInt(fileLineArray[2]));
                    nodeList.add(vertex);    
                System.out.println(fileLineArray[1] + "   " + fileLineArray[2] );
                }
            }
            
            fileLine = in.readLine();
            fileLineArray = fileLine.split(":");
            int numEdges = Integer.parseInt(fileLineArray[1].trim());
            System.out.println("edges : " + numEdges);
            for (int i = 0; i < numEdges; i++) {
                if (in.read() != -1) {

                    fileLineArray = in.readLine().split(",");
                    int weight = Integer.parseInt(fileLineArray[2].trim());                    
                    int u = Integer.parseInt(fileLineArray[1].trim());
                    int v = Integer.parseInt(fileLineArray[0].trim());
                    System.out.println(fileLineArray[0] + "   " + fileLineArray[1] );
                    WeightedEdge edge = new WeightedEdge(v, u, weight);                    
                    edgeList.add(edge);
                    
                }
            }           
        } catch (Exception ee) {
            System.out.println("import: " + ee);
        }
        
        graph = new WeightedGraph<Node>(edgeList, nodeList);
        for (WeightedEdge eee : edgeList) {
            graph.addEdge(eee.u, eee.v, eee.weight);
        }
        view.setGraph(graph);
        view.addNodesToGraph();
        view.repaint();
        System.out.println(edgeList.size());
    }
    
    void exportAction(ActionEvent e){
        
        JFileChooser chooser = new JFileChooser();
        chooser.showSaveDialog(exportButton);
        File file = chooser.getSelectedFile();        
        
        //Write to file
        try {
            
            // Create file  -> pass the file path into the FileWriter object
            FileWriter fstream = new FileWriter(file.getAbsolutePath());
            BufferedWriter out = new BufferedWriter(fstream);
            
            //Write File
            out.write(" vertices:" + nodeList.size() + "\n");
            
            for(Node v : nodeList){
                out.write(" " + v.getName() + "," + v.getX() + "," + v.getY() + "\n");
            }
            
            out.write(" edges:" + edgeList.size() + "\n ");
            
            for(WeightedEdge ee : edgeList){
                out.write(" " + ee.u + "," + ee.v + "," + ee.weight +"\n");
            }
            
            //Close the output stream
            out.close();
            
        } catch (Exception ee) {//Catch exception if any
            System.err.println("Error: " + ee.getMessage());
        }        
    }
    
    void generateNodesAction(ActionEvent e) {

//        if(isGenerating){
//            isGenerating = false;
//        }else{
//            isGenerating = true;
//        }
        Random generator = new Random(new Date().getTime());

        for (long i = 0; i < 10000; i++) {
            graph.addVertex(new Node(String.valueOf(numOfGenNodes), generator.nextInt(1150), generator.nextInt(550)));
            view.addNodesToGraph();
            //view.repaint();
            numOfGenNodes++;
            if (numOfGenNodes % 100 == 0) {
                System.out.println(numOfGenNodes);
            }
            //System.out.println(numOfGenNodes);
        }

    }

    void clearGraphAction(ActionEvent e){
        //Clear contents of vertex and edge lists
        edgeList.clear();
        nodeList.clear();
        graph = new WeightedGraph<Node>(edgeList, nodeList);
        view.setGraph(graph);
        view.addNodesToGraph();
        view.repaint();
    }
    
    void addDefaultNodes() {
        nodeList.add(new Node("Seattle", 75, 50));
        nodeList.add(new Node("Denver", 275, 175));
        nodeList.add(new Node("San Francisco", 50, 210));
        nodeList.add(new Node("Los Angeles", 75, 275));        
        nodeList.add(new Node("Kansas City", 400, 245));
        nodeList.add(new Node("Chicago", 450, 100));
        nodeList.add(new Node("Boston", 700, 80));
        nodeList.add(new Node("New York", 675, 120));
        nodeList.add(new Node("Atlanta", 575, 295));
        nodeList.add(new Node("Miami", 600, 400));
        nodeList.add(new Node("Dallas", 408, 325));
        nodeList.add(new Node("Houston", 450, 360));
    }

    void addDefaultEdges() {
        // Edge array for graph in Figure 27.1
//        edgeList.add(new WeightedEdge(0, 1, 807));
//        edgeList.add(new WeightedEdge(0, 3, 1331));
//        edgeList.add(new WeightedEdge(0, 5, 2097));
//
//        edgeList.add(new WeightedEdge(1, 0, 807));
//        edgeList.add(new WeightedEdge(1, 2, 381));
//        edgeList.add(new WeightedEdge(1, 3, 1267));
//
//        edgeList.add(new WeightedEdge(2, 1, 381));
//        edgeList.add(new WeightedEdge(2, 3, 1015));
//        edgeList.add(new WeightedEdge(2, 4, 1663));
//        edgeList.add(new WeightedEdge(2, 10, 1435));
//
//        edgeList.add(new WeightedEdge(3, 0, 1331));
//        edgeList.add(new WeightedEdge(3, 1, 1267));
//        edgeList.add(new WeightedEdge(3, 2, 1015));
//        edgeList.add(new WeightedEdge(3, 4, 599));
//        edgeList.add(new WeightedEdge(3, 5, 1003));
//
//        edgeList.add(new WeightedEdge(4, 2, 1663));
//        edgeList.add(new WeightedEdge(4, 3, 599));
//        edgeList.add(new WeightedEdge(4, 5, 533));
//        edgeList.add(new WeightedEdge(4, 7, 1260));
//        edgeList.add(new WeightedEdge(4, 8, 864));
//        edgeList.add(new WeightedEdge(4, 10, 496));
//
//        edgeList.add(new WeightedEdge(5, 0, 2097));
//        edgeList.add(new WeightedEdge(5, 3, 1003));
//        edgeList.add(new WeightedEdge(5, 4, 533));
//        edgeList.add(new WeightedEdge(5, 6, 983));
//        edgeList.add(new WeightedEdge(5, 7, 787));
//
//        edgeList.add(new WeightedEdge(6, 5, 983));
//        edgeList.add(new WeightedEdge(6, 7, 214));
//
//        edgeList.add(new WeightedEdge(7, 4, 1260));
//        edgeList.add(new WeightedEdge(7, 5, 787));
//        edgeList.add(new WeightedEdge(7, 6, 214));
//        edgeList.add(new WeightedEdge(7, 8, 888));

        edgeList.add(new WeightedEdge(8, 4, 864));
        edgeList.add(new WeightedEdge(8, 7, 888));
        edgeList.add(new WeightedEdge(8, 9, 661));
        edgeList.add(new WeightedEdge(8, 10, 781));
        edgeList.add(new WeightedEdge(8, 11, 810));

//        edgeList.add(new WeightedEdge(9, 8, 661));
//        edgeList.add(new WeightedEdge(9, 11, 1187));
//
//        edgeList.add(new WeightedEdge(10, 2, 1435));
//        edgeList.add(new WeightedEdge(10, 4, 496));
//        edgeList.add(new WeightedEdge(10, 8, 781));
//        edgeList.add(new WeightedEdge(10, 11, 239));
//
//        edgeList.add(new WeightedEdge(11, 8, 810));
//        edgeList.add(new WeightedEdge(11, 9, 1187));
//        edgeList.add(new WeightedEdge(11, 10, 239));

    }
}
    